package com.springboot2.security.base.security.filter;

import com.springboot2.security.base.constant.Constant;
import com.springboot2.security.entity.po.RoleInfo;
import com.springboot2.security.entity.po.WebResource;
import com.springboot2.security.mappers.resource.WebResourceDao;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 用户加载资源服务配置类
 *
 * @param
 * @author liucancan
 * @date 12:55 2018/12/12
 * @return
 */
@Service
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
    private org.slf4j.Logger LOG = LoggerFactory.getLogger(getClass());

    @Autowired
    private WebResourceDao webResourceDao;

    /**
     * 项目启动时加载所有资源信息，可以放到本方法的getAttributes()中，每次请求都重新获取所有资源，无需重启项目，但是性能不好。
     *
     * @return void
     * @date 14:27 2018/12/13
     */
    @PostConstruct
    public void loadResourceDefine() {
        resourceMap = new HashMap<>(16);
        List<WebResource> resources = webResourceDao.selectResources();
        for (WebResource resource : resources) {
            if (StringUtils.isEmpty(resource.getPermission())) {
                continue;
            }
            Collection<ConfigAttribute> configAttributes = new ArrayList<>();
            if (resource.getRoles() != null && resource.getRoles().size() > 0) {
                for (RoleInfo roleInfo : resource.getRoles()) {
                    ConfigAttribute configAttribute = new SecurityConfig(roleInfo.getName());
                    configAttributes.add(configAttribute);
                }
                resourceMap.put(resource.getPermission(), configAttributes);
            } else {
                ConfigAttribute configAttribute = new SecurityConfig(Constant.NO_AUTHORITY);
                configAttributes.add(configAttribute);
                resourceMap.put(resource.getPermission(), configAttributes);
            }
        }
        LOG.info("security info load success!!");
    }

    /**
     * 每一个url请求都会经过getAttributes方法，通过url获取所需要的角色或权限标识，如果获取为空说明，系统没有改资源，则不会进行鉴权
     * 即不会执行MyAccessDecisionManager的decide方法，默认是有权限的。
     * 例如，页面中<div sec:authorize-url="/sys/nonetest">没有资源就会显示</>，系统没有/sys/nonetest的资源，div中的内容就会显示。
     *
     * @param object
     * @return java.util.Collection<org.springframework.security.access.ConfigAttribute>
     * @date 14:32 2018/12/13
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        loadResourceDefine();
        String requestUrl = ((FilterInvocation) object).getRequestUrl();
        /**
         * 返回当前 url  所需要的角色或权限
         * 1. 判断是否含有此资源
         *  否 return null
         *  是
         *      null 抛出异常
         *      not null return configAttributes
         */
        boolean contain = resourceMap.containsKey(requestUrl);
        if (!contain) {
            return null;
        } else {
            Collection<ConfigAttribute> configAttributes = resourceMap.get(requestUrl);
            return configAttributes;
        }
    }


    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
